﻿using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Text.RegularExpressions;


namespace SolutionGeneratorHelper
{
    internal class Solution : CommonNode
    {
        private static readonly Regex SolutionInfoDeclaration = new Regex("Microsoft[\\n\\s\\S]+#[^\\f\\n\\r\\t\\v]+");
        private static readonly Regex ProjectDeclaration = new Regex("Project\\(\"\\{(?<solutionGuid>.*)\\}\"\\) = \"(?<projectName>.*)\", \"(?<fileName>.*)\", \"\\{(?<projectGuid>.*)\\}\"");
        private static readonly Regex SectionDeclaration = new Regex("GlobalSection\\((?<label>.*)\\) = (?<type>[^\f\n\r\t\v]+).*\\n(?<content>[\\s\\S]*?)EndGlobalSection");

        private string m_solutionInfo;
        public string SolutionInfo { get => m_solutionInfo; set => m_solutionInfo = value; }

        private List<SolutionConfig> m_solutionConfigs;
        private Dictionary<Project, List<Project>> m_dictDependencies;

        private List<Project> m_projects;
        internal List<Project> Projects { get => m_projects; set => m_projects = value; }

        private List<ProjectRef> m_projectRefs;
        internal List<ProjectRef> ProjectRefs { get => m_projectRefs; }

        // solution GUID
        private string m_solutionGuid;
        internal string SolutionGuid { get => m_solutionGuid; set => m_solutionGuid = value; }

        // solution Name
        private string m_solutionName;
        public string SolutionName { get => m_solutionName; set => m_solutionName = value; }

        // solution platforms config;
        private SolutionConfig m_solutionPlatforms;
        internal SolutionConfig SolutionPlatforms { get => m_solutionPlatforms; set => m_solutionPlatforms = value; }

        // project platforms config
        private SolutionConfig m_projectPlatforms;
        internal SolutionConfig ProjectPlatforms { get => m_projectPlatforms; set => m_projectPlatforms = value; }


        protected Solution()
        {
            m_projects = new List<Project>();
            m_projectRefs = new List<ProjectRef>();
            m_solutionConfigs = new List<SolutionConfig>();
            m_dictDependencies = new Dictionary<Project, List<Project>>();
        }


        #region parse solution

        public static T ParseSolution<T>(string _content) where T : Solution, new()
        {
            T t = new T();
            ParseSolution(t, _content);
            return t;
        }

        protected static Solution ParseSolutionFile(Solution _solution, string _solutionFile)
        {

            DirectoryInfo dirInfo = Directory.GetParent(Environment.CurrentDirectory);
            string path = dirInfo.Parent.FullName + "/" + _solutionFile;
            string content = File.ReadAllText(path);
            _solution.ParseSolution(content);
            return null;
        }

        internal virtual void ParseSolutionFile(string _solutionFile)
        {
            ParseSolutionFile(this, _solutionFile);
        }

        protected static void ParseSolution(Solution _solution, string _content)
        {
            MatchCollection mc = ProjectDeclaration.Matches(_content);
            foreach (Match item in mc)
            {
                if (null == _solution.SolutionGuid)
                    _solution.SolutionGuid = item.Groups["solutionGuid"].ToString();

                _solution.InsertProject(item.Groups["fileName"].ToString(), false);

                _solution.InsertOrUpdateProjectRef(new ProjectRef(
                    new Field("FileName", item.Groups["fileName"].ToString()),
                    new Field("ProjectName", item.Groups["projectName"].ToString()),
                    new Field("ProjectGuid", item.Groups["projectGuid"].ToString()),
                    new Field("SolutionGuid", item.Groups["solutionGuid"].ToString())
                ));
            }

            mc = SectionDeclaration.Matches(_content);
            foreach (Match item in mc)
            {
                SolutionConfig sc = SolutionConfig.GetSolutionConfigType(
                    new Field("label", item.Groups["label"].ToString()),
                    new Field("type", item.Groups["type"].ToString())
                );
                _solution.m_solutionConfigs.Add(sc);
                _solution.InsertChild(sc);
                sc.ParseSolutionConfig(item.Groups["content"].ToString());
            }

            Match info = SolutionInfoDeclaration.Match(_content);
            _solution.SolutionInfo = info.Value;
        }

        internal virtual void ParseSolution(string _content)
        {
            ParseSolution(this, _content);
        }

        #endregion


        #region Project Interface

        internal virtual bool ContainsProject(Project _proj)
        {
            bool rt = false;
            foreach (Project proj in Projects)
            {
                if (_proj.ProjectGuid.Value == proj.ProjectGuid.Value)
                {
                    rt = true;
                    break;
                }
            }
            return rt;
        }

        internal virtual Project GetProject(Field _projGuid)
        {
            Project rt = null;
            foreach (Project proj in Projects)
            {
                if (_projGuid.Value == proj.ProjectGuid.Value)
                {
                    rt = proj;
                    break;
                }
            }
            return rt;
        }

        internal virtual void InsertProject(Project _proj, bool _bUpdate = true)
        {
            if (ContainsProject(_proj))
            {
                return;
                // throw new Exception(string.Format("Insert an exist project:\n\tName is {0}\n\tGuid is {1}", _proj.FileName.Value, _proj.ProjectGuid.Value));
            }
            Projects.Add(_proj);

            if (!_bUpdate)
                return;

            ProjectRef projRef = _proj.GetCurrentProjectRef();
            InsertOrUpdateProjectRef(projRef);

            if (null == ProjectPlatforms)
                return;
            ProjectPlatformsConfig ppc = (ProjectPlatformsConfig)ProjectPlatforms;
            ppc.UpdateConfig(projRef, "Debug", "ActiveCfg", "Any CPU");
            ppc.UpdateConfig(projRef, "Debug", "Build.0", "Any CPU");
            ppc.UpdateConfig(projRef, "Release", "ActiveCfg", "Any CPU");
            ppc.UpdateConfig(projRef, "Release", "Build.0", "Any CPU");
        }

        internal virtual void InsertProject(string _projPath, bool _bUpdate = true)
        {
            Project proj = new Project();
            proj.ParseProjectFile(_projPath);
            if(proj.Valid)
                InsertProject(proj, _bUpdate);
        }

        internal virtual void InsertProject<T>(string _projPath, bool _bUpdate = true) where T : Project, new()
        {
            T t = new T();
            t.ParseProjectFile(_projPath);
            if (t.Valid)
                InsertProject(t, _bUpdate);
        }

        #endregion


        #region Project Ref Interface

        internal virtual bool ContainsProjectRef(ProjectRef _projRef)
        {
            bool rt = false;
            foreach(ProjectRef projRef in ProjectRefs)
            {
                if (_projRef.ProjectGuid.Value == projRef.ProjectGuid.Value)
                {
                    rt = true;
                    break;
                }
            }
            return rt;
        }

        internal virtual void InsertOrUpdateProjectRef(ProjectRef _projRef)
        {
            if (ContainsProjectRef(_projRef))
            {
                Console.WriteLine("Insert an exist project ref, so update it");
                ProjectRef projRef = GetProjectRef(_projRef.ProjectGuid);
                projRef.FileName = _projRef.FileName;
                projRef.ProjectName = _projRef.ProjectName;
                projRef.SolutionGuid.Value = SolutionGuid;
            }
            else
            {
                Console.WriteLine("Insert a project ref");
                if (null == _projRef.SolutionGuid)
                    _projRef.SolutionGuid = new Field("SolutionGuid", SolutionGuid);
                ProjectRefs.Add(_projRef);
            }

        }

        internal virtual ProjectRef GetProjectRef(Field _projGuid)
        {
            ProjectRef rt = null;
            foreach (ProjectRef projRef in ProjectRefs)
            {
                if (_projGuid.Value == projRef.ProjectGuid.Value)
                {
                    rt = projRef;
                    break;
                }
            }
            return rt;
        }

        #endregion

        public override void Serialize(StringBuilder _sb)
        {
            _sb.Append("\r\n");
            _sb.Append(SolutionInfo);
            foreach(ProjectRef projRef in ProjectRefs)
            {
                projRef.Serialize(_sb);
            }
            _sb.Append("\r\nGlobal");
            base.Serialize(_sb);
            _sb.Append("\r\nEndGlobal\r\n");
            // Console.WriteLine(_sb);
        }
    }
}
